# Copyright 2010 Michael Murr
#
# This file is part of LibForensics.
#
# LibForensics is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# LibForensics is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with LibForensics.  If not, see <http://www.gnu.org/licenses/>.

"""Bit-oriented data types."""

# stdlib imports
from collections import OrderedDict
from ctypes import (
    c_int8, c_uint8, c_int16, c_uint16, c_int32, c_uint32, c_int64, c_uint64
)

# local imports
from lf.dtypes.base import DataType
from lf.dtypes.basic import Basic

__docformat__ = "restructuredtext en"
__all__ = [
    "bit", "bits", "BitType8", "BitTypeU8", "BitType16",
    "BitTypeU16", "BitType32", "BitTypeU32", "BitType64", "BitTypeU64"
]

class bits(DataType):
    """Represents one or more bits.

    .. attribute:: _size_

        The number of bits in the data type.
    """

    def __init__(self, size=1):
        """Initializes a bits object.

        :type size: int
        :param size: The number of bits in the data type.

        """
        super(bits, self).__init__()
        self._size_ = size
    # end def __init__
# end class bits

class bit(bits):
    """A convenience class to represent a single bit."""

    _size_ = 1
# end class bit

class MetaBitType(type):
    """Metaclass to build _fields_ attribute of BitType data types"""
    @classmethod
    def __prepare__(metacls, name, bases):
        """Makes a BitType's dict an OrderedDict"""

        return OrderedDict()
    # end class __prepare__

    def __new__(cls, name, bases, clsdict):
        """Builds the _fields_ attribute"""

        new_cls = type.__new__(cls, name, bases, clsdict)
        new_clsdict = new_cls.__dict__

        fields = new_clsdict.get("_fields_")
        mro = new_cls.__mro__

        if fields is None:
            fields = list()

            klsdicts = [klass.__dict__ for klass in mro]

            # Find the most recent _fields_, since it should contain every
            # _fields_ before it.
            for klsdict in klsdicts:
                _fields_ = klsdict.get("_fields_")
                if _fields_:
                    break
                # end if
            else:
                _fields_ = list()
            # end for

            fields.extend(_fields_)
            for (key, value) in clsdict.items():
                if not key.startswith("_"):
                    fields.append((key, value))
                # end if
            # end for

            new_cls._fields_ = fields
        # end if

        return new_cls
    # end def __new__

# end class MetaBitType

class BitType(Basic, metaclass=MetaBitType):
    """A container class for bits.

    This class is used to allow bits to be used as a :class:`Primitive` class.

    .. attribute:: _int_type_

        The ctypes integer type that encapsulates the bits.

    .. attribute:: _fields_

        A list of the fields in the BitType.  If this is None (or not present)
        it is automatically generated by a metaclass.
    """

    pass
# end class BitType

class BitType8(BitType):
    """A bit type represented by a signed 8-bit integer."""

    _size_ = 1
    _ctype_ = _int_type_ = c_int8
# end class BitType8

class BitTypeU8(BitType):
    """A bit type represented by an unsigned 8-bit integer."""

    _size_ = 1
    _ctype_ = _int_type_ = c_uint8
# end class BitTypeU8

class BitType16(BitType):
    """A bit type represented by a signed 16-bit integer."""

    _size_ = 2
    _ctype_ = _int_type_ = c_int16
# end class bits16

class BitTypeU16(BitType):
    """A bit type represented by an unsigned 16-bit integer."""

    _size_ = 2
    _ctype_ = _int_type_ = c_uint16
# end class BitTypeU16

class BitType32(BitType):
    """A bit type represented by a signed 32-bit integer."""

    _size_ = 4
    _ctype_ = _int_type_ = c_int32
# end class BitType32

class BitTypeU32(BitType):
    """A bit type represented by an unsigned 32-bit integer."""

    _size_ = 4
    _ctype_ = _int_type_ = c_uint32
# end class BitTypeU32

class BitType64(BitType):
    """A bit type represented by a signed 64-bit integer."""

    _size_ = 8
    _ctype_ = _int_type_ = c_int64
# end class BitType64

class BitTypeU64(BitType):
    """A bit type represented by an unsigned 64-bit integer."""

    _size_ = 8
    _ctype_ = _int_type_ = c_uint64
# end class BitTypeU64
